#pragma once

//*****************************************************************************
// cFile
//*****************************************************************************
class cFile {
public:
	cFile() : m_Pos(0) {
	}
	
	void Set(void *Src, const int Size, const char *FilePn = NULL);
	void Copy(const void *Src, const int Size, const char *FilePn = NULL);

	void Clear();
	
	const cStr & GetFilePn() const { return m_FilePn; }
	void SetFilePn(const char *FilePn) { m_FilePn = FilePn; }
	
	bool IsEof() const { return m_Pos >= m_Data.Count(); }
	size_t Size() const { return m_Data.Size(); }
	int GetPos() const { return m_Pos; }
	
	// Moves the data position pointer to a new location that is "Offset" bytes from origin.
	// Returns the offset, in bytes, of the new position from the beginning of the data.
	int SeekCur(const int Offset) const;
	int SeekEnd(const int Offset) const;
	int SetPos(const int Pos) const;
	
	// Returns the number of bytes actually read, which may be less than MaxSize if the end of data
	// is encountered before reaching MaxSize.
	int Read(void *To, const int MaxSize) const;
	void Write(const void *Fm, const int Size);
	
	const void * ToPtr() const { return m_Data.ToPtr(); } // Can be NULL
	void * ToPtr() { return m_Data.ToPtr(); }
	
	byte ReadByte() const;
	bool ReadByte(byte *b) const;
	void WriteByte(const byte b);
	
	short ReadShort() const;
	bool ReadShort(short *s) const;
	void WriteShort(const short s);

	word ReadWord() const;
	bool ReadWord(word *w) const;
	void WriteWord(const word w);

	int  ReadInt() const;
	bool ReadInt(int *i) const;
	void WriteInt(const int i);

	dword ReadDword() const;
	bool ReadDword(dword *dw) const;
	void WriteDword(const dword dw);

	float ReadFloat() const;
	bool ReadFloat(float *f) const;
	void WriteFloat(const float f);

	double ReadDouble() const;
	bool ReadDouble(double *d) const;
	void WriteDouble(const double d);
	
	bool ReadString(cStr *, const char *Terminators = "\r\n") const;
	void WriteString(const char *);
private:
	cStr m_FilePn;
	cList<char> m_Data;
	mutable int m_Pos;
};

// cFile::SeekCur
inline int cFile::SeekCur(const int Offset) const {
	m_Pos = cMath::Clamp(m_Pos + Offset, 0, m_Data.Count());
	return m_Pos;
}

// cFile::SeekEnd
inline int cFile::SeekEnd(const int Offset) const {
	m_Pos = cMath::Clamp(m_Data.Count() + Offset, 0, m_Data.Count());
	return m_Pos;
}

// cFile::SetPos
inline int cFile::SetPos(const int Pos) const {
	m_Pos = cMath::Clamp(Pos, 0, m_Data.Count());
	return m_Pos;
}

// cFile::ReadByte
inline bool cFile::ReadByte(byte *b) const {
	if(m_Pos >= m_Data.Count()) {
		return false;
	}
	*b = m_Data[m_Pos];
	m_Pos++;
	return true;
}
inline byte cFile::ReadByte() const {
	if(m_Pos >= m_Data.Count()) {
		return 0;
	}
	byte b = m_Data[m_Pos];
	m_Pos++;
	return b;
}

// cFile::WriteByte
inline void cFile::WriteByte(const byte b) {
	m_Data.Insert(m_Pos, b);
	m_Pos++;
}

// cFile::ReadShort
inline bool cFile::ReadShort(short *s) const {
	if(m_Pos + 1 >= m_Data.Count()) {
		return false;
	}
	*s = *(short *)(&m_Data[m_Pos]);
	m_Pos += 2;
	return true;
}
inline short cFile::ReadShort() const {
	if(m_Pos + 1 >= m_Data.Count()) {
		return 0;
	}
	short s = *(short *)(&m_Data[m_Pos]);
	m_Pos += 2;
	return s;
}

// cFile::WriteShort
inline void cFile::WriteShort(const short s) {
	m_Data.Insert(m_Pos, 0, 2);
	*(short *)&m_Data[m_Pos] = s;
	m_Pos += 2;
}

// cFile::ReadWord
inline bool cFile::ReadWord(word *w) const {
	if(m_Pos + 1 >= m_Data.Count()) {
		return false;
	}
	*w = *(word *)(&m_Data[m_Pos]);
	m_Pos += 2;
	return true;
}
inline word cFile::ReadWord() const {
	if(m_Pos + 1 >= m_Data.Count()) {
		return 0;
	}
	word w = *(word *)(&m_Data[m_Pos]);
	m_Pos += 2;
	return w;
}

// cFile::WriteWord
inline void cFile::WriteWord(const word w) {
	m_Data.Insert(m_Pos, 0, 2);
	*(word *)&m_Data[m_Pos] = w;
	m_Pos += 2;
}

// cFile::ReadInt
inline bool cFile::ReadInt(int *i) const {
	if(m_Pos + 3 >= m_Data.Count()) {
		return false;
	}
	*i = *(int *)(&m_Data[m_Pos]);
	m_Pos += 4;
	return true;
}
inline int cFile::ReadInt() const {
	if(m_Pos + 3 >= m_Data.Count()) {
		return 0;
	}
	int i = *(int *)(&m_Data[m_Pos]);
	m_Pos += 4;
	return i;
}

// cFile::WriteInt
inline void cFile::WriteInt(const int i) {
	m_Data.Insert(m_Pos, 0, 4);
	*(int *)&m_Data[m_Pos] = i;
	m_Pos += 4;
}

// cFile::ReadDword
inline bool cFile::ReadDword(dword *dw) const {
	if(m_Pos + 3 >= m_Data.Count()) {
		return false;
	}
	*dw = *(dword *)(&m_Data[m_Pos]);
	m_Pos += 4;
	return true;
}
inline dword cFile::ReadDword() const {
	if(m_Pos + 3 >= m_Data.Count()) {
		return 0;
	}
	dword dw = *(dword *)(&m_Data[m_Pos]);
	m_Pos += 4;
	return dw;
}

// cFile::WriteDword
inline void cFile::WriteDword(const dword dw) {
	m_Data.Insert(m_Pos, 0, 4);
	*(dword *)&m_Data[m_Pos] = dw;
	m_Pos += 4;
}

// cFile::ReadFloat
inline bool cFile::ReadFloat(float *f) const {
	if(m_Pos + 3 >= m_Data.Count()) {
		return false;
	}
	*f = *(float *)(&m_Data[m_Pos]);
	m_Pos += 4;
	return true;
}
inline float cFile::ReadFloat() const {
	if(m_Pos + 3 >= m_Data.Count()) {
		return 0;
	}
	float f = *(float *)(&m_Data[m_Pos]);
	m_Pos += 4;
	return f;
}

// cFile::WriteFloat
inline void cFile::WriteFloat(const float f) {
	m_Data.Insert(m_Pos, 0, 4);
	*(float *)&m_Data[m_Pos] = f;
	m_Pos += 4;
}

// cFile::ReadDouble
inline bool cFile::ReadDouble(double *d) const {
	if(m_Pos + 7 >= m_Data.Count()) {
		return 0.0;
	}
	*d = *(double *)(&m_Data[m_Pos]);
	m_Pos += 8;
	return true;
}
inline double cFile::ReadDouble() const {
	if(m_Pos + 7 >= m_Data.Count()) {
		return 0.0;
	}
	double d = *(double *)(&m_Data[m_Pos]);
	m_Pos += 8;
	return d;
}

// cFile::WriteDouble
inline void cFile::WriteDouble(const double d) {
	m_Data.Insert(m_Pos, 0, 8);
	*(double *)&m_Data[m_Pos] = d;
	m_Pos += 8;
}